﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Net.Sockets;
using System.Net;
using System.Threading.Tasks;
using System.Diagnostics;
using Newtonsoft.Json;
using System.Threading;

namespace YGame.GatewayServer
{
    delegate void ConnectHandler(ConnectArgs args);
    class ConnectArgs
    {
        public string IP { get; set; }
        public int Port { get; set; }
        //挂起连接队列的最大长度。
        public int Backlog { get; set; }


        public Socket Listener { get; set; }
        public Socket Connect { get; set; }
        public ConnectHandler ConnectHandler { get; set; }
        public Dictionary<string, IConnect> ConnectContainer { get; set; }
    }


    class MainModel
    {

        Socket ServerListener;
        Socket ClientListener;

        /// <summary>
        /// 客户端连接列表
        /// </summary>
        Dictionary<string, IConnect> ClientConnects;


        /// <summary>
        /// 服务器连接列表
        /// </summary>
        Dictionary<string, IConnect> ServerConnects;

        ServerConnect DefaultServerConnect;

        public ManualResetEvent ClientCloseSignal;

        public MainModel()
        {
            ClientCloseSignal = new ManualResetEvent(false);
            ClientCloseSignal.Reset();

            ClientConnects = new Dictionary<string, IConnect>();
            ServerConnects = new Dictionary<string, IConnect>();
        }

        public bool Start()
        {

            // 从配置读入监听端口列表
            ConnectArgs serverArgs = new ConnectArgs() { ConnectHandler = ServerConnectThead, ConnectContainer = ServerConnects };
            serverArgs.IP = ConfigurationManager.AppSettings["ServerIP"];
            serverArgs.Port = Convert.ToInt32(ConfigurationManager.AppSettings["ServerPort"]);
            ServerListener = CreateListener(serverArgs);

            // 从配置读入监听端口列表，用半角逗号分割
            string clientIP = ConfigurationManager.AppSettings["ClientIP"];
            string clientPort = ConfigurationManager.AppSettings["ClientPort"];
            // 从配置读入监听端口列表
            ConnectArgs clientArgs = new ConnectArgs() { ConnectHandler = ClientConnectThead, ConnectContainer = ClientConnects };
            clientArgs.IP = ConfigurationManager.AppSettings["ClientIP"];
            clientArgs.Port = Convert.ToInt32(ConfigurationManager.AppSettings["ClientPort"]);
            ClientListener = CreateListener(clientArgs);
            return true;
        }


        public Socket CreateListener(ConnectArgs args)
        {
            try
            {
                Trace.WriteLine(String.Format("start listen {0}:{1}", args.IP, args.Port));
                IPAddress[] adds = Dns.GetHostAddresses(args.IP);

                //本地终结点
                IPEndPoint iep = new IPEndPoint(adds[0], args.Port);

                //实例化内成员sock
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                // 绑定
                socket.Bind(iep);

                // 设置为监听状态
                socket.Listen(10);

                // 启动异步连接处理
                args.Listener = socket;
                socket.BeginAccept(onAccept, args);
                return socket;
            }
            catch (Exception ex)
            {
                Trace.WriteLine(String.Format("start listen {0}:{1} error.\r\n{2}", args.IP, args.Port, ex.Message));
                return null;
            }
        }



        /// <summary>
        /// 异步连接处理
        /// </summary>
        /// <param name="ar"></param>
        public void onAccept(IAsyncResult ar)
        {
            ConnectArgs args = (ConnectArgs)ar.AsyncState;
            Socket listener = args.Listener;
            args.Connect = listener.EndAccept(ar);
            // 重新开始监听
            listener.BeginAccept(onAccept, listener);

            // 启动连接处理
            Task.Factory.StartNew(() => { args.ConnectHandler(args); });
        }


        /// <summary>
        /// 接受处理线程
        /// </summary>
        /// <param name="conn"></param>
        public void ClientConnectThead(ConnectArgs args)
        {
            Socket conn = args.Connect;
            byte[] buffer = new byte[1024 * 8];
            Trace.WriteLine(String.Format("{0}: receive connect: {1}", conn.LocalEndPoint.ToString(), conn.RemoteEndPoint.ToString()));
            string clientInfo=conn.RemoteEndPoint.ToString();
            string token=null;
            try
            {
                // 先接受第一次连接确认请求，如果合服，就放入连接容器中
                ClientConnect clientConnect = CheckClientConnect(conn);
                token = clientConnect.Token;
                args.ConnectContainer.Add(clientConnect.Token, clientConnect);

                // 一直接收数据，直到结束
                while (true)
                {
                    int n = conn.Receive(buffer);
                    if (n == 0)
                    {
                        Trace.WriteLine(String.Format("close connect {0}", clientInfo));
                        return;
                    }
                    else
                    {
                        string json = System.Text.Encoding.Default.GetString(buffer);
                        Trace.WriteLine(String.Format("{0}: receive {1} bytes.\r\n{2} ", clientInfo, n, json));
                        // 解包消息
                        ClientMessage msg = JsonConvert.DeserializeObject<ClientMessage>(json);

                        // 根据Token，查询场景服


                        // 消息转发到场景服
                    }
                }
            }
            catch (SocketException ex)
            {
                Trace.WriteLine(String.Format("socket error: {0}. {1} ", clientInfo, ex.Message));
            }
            catch (Exception ex)
            {
                Trace.WriteLine(String.Format("error: {0}. {1} ", clientInfo, ex.Message));
            }
            finally
            {
                if (token!=null)
                {
                    args.ConnectContainer.Remove(token);
                }
                ClientCloseSignal.Set();
            }

        }

        private ClientConnect CheckClientConnect(Socket conn)
        {
            byte[] buffer = new byte[1024];
            int n = conn.Receive(buffer);
            if (n == 0)
            {
                throw new ApplicationException("close connect in check");
            }
            string json = System.Text.Encoding.Default.GetString(buffer, 0, n);
            Trace.WriteLine(String.Format("{0}: receive {1} bytes.\r\n{2} ", conn.RemoteEndPoint.ToString(), n, json));
            // 解包消息
            ClientMessage msg = JsonConvert.DeserializeObject<ClientMessage>(json);

            ClientConnect client = new ClientConnect();
            client.Token = msg.Token;
            client.Connect = conn;

            // TODO: 在登录服中转过来的token查询userid等信息
            // 这里先随便设置吧
            int newid = ClientConnects.Count;
            client.UserID = newid.ToString();
            client.UserName = String.Format("User{0:4}", newid);
            client.ActorID = client.UserID;
            client.ActorName = client.UserName;
            client.ServerConnect = DefaultServerConnect;

            ClientMessage stRsp=new ClientMessage();
            stRsp.ID = "0001";
            stRsp.Body = "ok";
            json = JsonConvert.SerializeObject(stRsp);
            conn.Send(Encoding.Default.GetBytes(json));

            return client;
        }



        /// <summary>
        /// 接受处理线程
        /// </summary>
        /// <param name="conn"></param>
        public void ServerConnectThead(ConnectArgs args)
        {
            Socket conn = args.Connect;
            byte[] buffer = new byte[1024 * 8];
            Trace.WriteLine(String.Format("{0}: receive connect: {1}", conn.LocalEndPoint.ToString(), conn.RemoteEndPoint.ToString()));
            try
            {

                // 一直接收数据，直到结束
                while (true)
                {
                    int n = conn.Receive(buffer);
                    if (n == 0)
                    {
                        Trace.WriteLine(String.Format("close connect {0}", conn.RemoteEndPoint.ToString()));
                        return;
                    }
                    else
                    {
                        string json = System.Text.Encoding.Default.GetString(buffer);
                        Trace.WriteLine(String.Format("{0}: receive {1} bytes.\r\n{2} ", conn.RemoteEndPoint.ToString(), n, json));
                        // 解包消息
                        ClientMessage msg = JsonConvert.DeserializeObject<ClientMessage>(json);

                        // 根据Token，查询场景服
                        IConnect client;
                        if (!ClientConnects.TryGetValue(msg.Token, out client))
                        {
                            throw new ApplicationException(String.Format("client token error: close connect {0}", conn.RemoteEndPoint.ToString()));
                        }

                        // 消息转发到场景服
                    }
                }
            }
            catch (SocketException ex)
            {
                Trace.WriteLine(String.Format("{0}: socket error: {1} {2}", conn.LocalEndPoint.ToString(), conn.RemoteEndPoint.ToString(), ex.Message));
            }
        }
    }
}
